#include <iostream>
#include <sys/epoll.h>
#include <cerrno>
#include <cstring>

#include "nocopy.hpp"
#include "log.hpp"

// 将Epoll的接口进行封装
class Epoller : public nocopy
{
    static const int size = 128;

public:
    Epoller()
    {
        // epoll_create创建，返回值用成员描述符接收，并且进行判断返回成功 or 失败
        _epfd = epoll_create(size);
        if (_epfd == -1)
        {
            lg(ERROR, "epoll_create 失败:%s", strerror(errno));
        }
        else
        {
            lg(INFO, "epoll_create 成功:%d", _epfd);
        }
    }

    // int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    // 判断是否已经就绪的接口
    int Epoll_Wait(struct epoll_event events[], int maxevents)
    {
        int n = epoll_wait(_epfd, events, maxevents, /*_timeout*/-1);
        return n;
    }

    // int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    // epfd在成员变量中，op和fd在外面传进来，event结构体中监控的时间类型也在外面传进来
    int Epoll_Update(int oper, int sock, uint32_t events)
    {
        int n = 0;
        // 根据是删除就绪队列中的还是增加或者修改就绪队列进行分开写
        // 删除：也就是根据传进来的oper参数进行宏判断;判断返回值
        if (oper == EPOLL_CTL_DEL)
        {
            // 因为是删除，不需要关心最后一个参数所对应的监控事件是啥，所以传nullptr，这也是为什么要将删除和其他两分开写的原因
            n = epoll_ctl(_epfd, oper, sock, nullptr);
            if (n != 0)
            {
                lg(ERROR, "epoll_ctl delete error!");
            }
        }
        else
        {
            // 增加或者修改监控事件
            // 填充epoll_event结构体，填充events和联合体中的fd
            struct epoll_event ev;
            ev.events = events;
            ev.data.fd = sock; // 为了方便后期得知是哪一个fd就绪了

            n = epoll_ctl(_epfd, oper, sock, &ev);
            if (n != 0)
            {
                lg(ERROR, "epoll_ctl add or mod error!");
            }
        }
        // 返回 epoll_ctl的返回值
        return n;
    }

    ~Epoller()
    {
        // 如果文件描述符是合法的，就关闭
        if (_epfd >= 0)
            close(_epfd);
    }

private:
    // epoll创建后的文件描述符
    int _epfd;
    // timeout
    int _timeout = 3000;
};